`timescale 1ns / 1ns

`include "defines.v"

module clint(

    input wire clk,
    input wire rst,

    // from peripheral 
    input wire[`INT_BUS] interrupt_flag_i,         // 婵炴垶鎼╅崢浠嬪蓟閸ャ劍缍囬柟鎯у暱瀵啿菐閸ワ絺鍋撻崘鎻掆枏(0x0000_000)

   // from id
    input wire[`InstBus] inst_i,             // (the next instruction )
    input wire[`InstAddrBus] inst_addr_i,     

     // from ex
    input wire jump_flag_i,
    input wire[`InstAddrBus] jump_addr_i,

    // from csr_reg
    input wire[`RegBus] data_i,              // CSR闁诲酣娼х€氼剟鎮洪妸鈺侀棷闁靛ǹ鍔庣紙濠氭煕韫囧鍔滈柡鍡欏枛閺佹捇鏁撻敓锟�?
    input wire[`RegBus] csr_mtvec,           // mtvec闁诲酣娼х€氼剟鎮洪妸鈺傛櫢闁跨噦鎷�?
    input wire[`RegBus] csr_mepc,            // mepc闁诲酣娼х€氼剟鎮洪妸鈺傛櫢闁跨噦鎷�?
    input wire[`RegBus] csr_mstatus,         // mstatus闁诲酣娼х€氼剟鎮洪妸鈺傛櫢闁跨噦鎷�?
    input wire global_int_en_i,              // 闂佺ǹ绻堥崝宀勬儑椤掍胶鈻旀い鎾跺枑閻掕棄霉閿濆牆鍔甸柛蹇旓耿瀵粙宕惰缁狅拷
    

    // to ctrl
    output wire hold_flag_o,                 // 濠电偟绻濋悞锕傚箰婢跺瞼妫柟绋垮閻ｎ垶鏌涚€ｎ偆鐭嬮柣鏍电秬缁犳稑顫忛悥锟�

    // to csr_reg
    output reg we_o,                         // 闂佸憡鍔栭…绯濺闁诲酣娼х€氼剟鎮洪妸鈺侀棷闁靛⿵濡囬崹濂告煥閻曞倹瀚�?
    output wire[`MemAddrBus] raddr_o,
    output reg[`MemAddrBus] waddr_o,         // 闂佸憡鍔栭…绯濺闁诲酣娼х€氼剟鎮洪妸鈺侀棷闁靛ǹ鍎查崟楣冩煥閻曞倹瀚�?
    output reg[`RegBus] data_o,              // 闂佸憡鍔栭…绯濺闁诲酣娼х€氼剟鎮洪妸鈺侀棷闁靛⿵闄勫▓鍫曟煥閻曞倹瀚�?
    

    // to ex
    output reg[`InstAddrBus] int_addr_o,     // 婵炴垶鎼╅崢浠嬪蓟閸ヮ剙绀傞柕澶堝劚缂嶆捇鏌涢敂鑺ョ凡婵炵》鎷�
    output reg int_assert_o                  // 婵炴垶鎼╅崢浠嬪蓟閸ヮ剙鍐€闁搞儜鍛啀

    );

    localparam S_INT_IDLE         = 4'b0001;
    localparam S_INT_SYNC_ASSERT  = 4'b0010;
    localparam S_INT_ASYNC_ASSERT = 4'b0100;
    localparam S_INT_MRET         = 4'b1000;

    localparam S_CSR_IDLE            = 5'b00001;
    localparam S_CSR_MSTATUS         = 5'b00010;
    localparam S_CSR_MEPC            = 5'b00100;
    localparam S_CSR_MSTATUS_MRET    = 5'b01000;
    localparam S_CSR_MCAUSE          = 5'b10000;

    reg[3:0] int_state;
    reg[4:0] csr_state;
    reg[`InstAddrBus] inst_addr;
    reg[31:0] cause;
    
    assign raddr_o = 12'hfff;
    

    assign hold_flag_o = ((int_state != S_INT_IDLE) || (csr_state != S_CSR_IDLE))? `HoldEnable: `HoldDisable;

    //enter interruption and judge the type of the of interrupt 
    always @(*)
        begin
            if (rst == `RstEnable)
                begin
                    int_state = S_INT_IDLE;
                end
            else if (inst_i == `INST_ECALL || inst_i == `INST_ECALL)//
                    begin
                        int_state = S_INT_SYNC_ASSERT;
                    end
                else if (interrupt_flag_i != `INT_NONE && global_int_en_i ==`True)
                    begin
                        int_state = S_INT_ASYNC_ASSERT;
                    end
                else if (inst_i == `INST_MRET)
                    begin
                        int_state = S_INT_MRET;
                    end
                else
                    begin
                        int_state = S_INT_IDLE;
                    end
        end
    //the switch of the csr writing 
    always @ (posedge clk)
        begin
            if (rst == `RstEnable)
                begin
                    csr_state <= S_CSR_IDLE;
                end
            else
                begin
                    case(csr_state)
                    S_CSR_IDLE:begin
                        
                        if (int_state == S_INT_SYNC_ASSERT)
                            begin
                                csr_state <= S_CSR_MEPC;
                                if (jump_flag_i == `JumpEnable)begin
                                    inst_addr <= jump_addr_i - 4'h4;
                                end
                                else begin
                                    inst_addr <= inst_addr_i;
                                end

                                case (inst_i)
                                    `INST_ECALL: begin
                                        cause <= 32'd11;
                                    end
                                    `INST_EBREAK: begin
                                        cause <= 32'd3;
                                    end
                                    default: begin
                                        cause <= 32'd10;
                                    end
                                endcase
                            end
                        else if (int_state == S_INT_ASYNC_ASSERT)begin
                            csr_state <= S_CSR_MEPC;
                            cause <= 32'h80000004;//timer 
                            if (jump_flag_i == `JumpEnable)begin
                                    inst_addr <= jump_addr_i;
                                end
                            else 
                                begin
                                    inst_addr <= inst_addr_i;
                                end
                        end
                        else if (int_state == S_INT_MRET)begin
                            csr_state <= S_CSR_MSTATUS_MRET;
                        end
                    end
                    S_CSR_MSTATUS:begin
                        csr_state <= S_CSR_MCAUSE;
                    end
                    S_CSR_MEPC:begin
                        csr_state <= S_CSR_MSTATUS;
                    end
                    S_CSR_MSTATUS_MRET:begin
                        csr_state <= S_CSR_IDLE;
                    end
                    S_CSR_MCAUSE:begin
                        csr_state <= S_CSR_IDLE;
                    end
                    default:
                        begin
                            
                        end
                    endcase
                end
            
        end

    //writing the csr reg
    always @ (posedge clk)
    begin
        if (rst == `RstEnable)
            begin
                we_o <= `WriteDisable;                    
                waddr_o <= `ZeroWord;              
                data_o <= `ZeroWord;
            end
        else 
            begin
                case(csr_state)
                    S_CSR_MSTATUS:begin
                        we_o <= `WriteEnable;                    
                        waddr_o <= {20'h0, `CSR_MSTATUS};              
                        data_o <= {csr_mstatus[31:8], csr_mstatus[3], csr_mstatus[6:4], 1'b0, csr_mstatus[2:0]};// can't support the interrupt nesting
                    end
                    S_CSR_MEPC:begin
                        we_o <= `WriteEnable;                    
                        waddr_o <= {20'h0, `CSR_MEPC};              
                        data_o <= inst_addr;
                    end
                    S_CSR_MSTATUS_MRET:begin
                        we_o <= `WriteEnable;                    
                        waddr_o <= {20'h0, `CSR_MSTATUS};              
                        data_o <= {csr_mstatus[31:8], 1'b1, csr_mstatus[6:4],csr_mstatus[7], csr_mstatus[2:0]};
                    end
                    S_CSR_MCAUSE:begin
                        we_o <= `WriteEnable;                    
                        waddr_o <= {20'h0, `CSR_MCAUSE};              
                        data_o <= cause;
                    end
                    default:begin
                        we_o <= `WriteDisable;                    
                        waddr_o <= `ZeroWord;              
                        data_o <= `ZeroWord;
                    end
                endcase
            end
    end

        

    // to send assert signal to the execution module 
    always @ (posedge clk) begin
        if (rst == `RstEnable)begin
            int_addr_o <= `ZeroWord;     
            int_assert_o <= `INT_DEASSERT;
        end
        else begin
            case(csr_state)
                S_CSR_MCAUSE:begin
                    int_addr_o <= csr_mtvec;     
                    int_assert_o <= `INT_ASSERT;
                end
                S_CSR_MSTATUS_MRET:begin
                    int_addr_o <= csr_mepc + 32'h4;     
                    int_assert_o <= `INT_ASSERT;
                end
                default:begin
                    int_addr_o <= `ZeroWord;     
                    int_assert_o <= `INT_DEASSERT;
                end
            endcase
        end 
    end

    

endmodule
